import { message } from "./index";
import config from "../config";
import { verifyEmpty, isObject, isFunction } from "./validate";
import storage from "./storage";

/**
 * @description: 发送请求模块(抽出公共部分)
 * @param {function} api 接口
 * @param {object} params 参数
 * @param {string} msg 提示信息
 * @param {boolean} request 是否使用接口msg
 * @return {Promise}
 */
export async function sendRequestModel(
  api,
  { params, msg = "请求", request = false, loading, ...config } = {}
) {
  let state = false,
    data = null;
  if (config.auto === undefined) config.auto = true;
  if (loading) uni.showLoading({ title: loading, mask: true });
  try {
    data = (await api(params)) || {};
    state = data?.code === 200;
    if (state) return Promise.resolve(data);
    else throw data;
  } catch (e) {
    //超时文本处理
    if (e?.timeout) msg += (config.auto = false) || "超时";
    console.error(e);
    return Promise.reject(e);
  } finally {
    //request为true时,使用接口返回的msg
    if (request && !["Success"].includes(data?.msg)) config.request = data?.msg;
    if (msg || config.request) message(msg, state, config);
    else if (loading) uni.hideLoading();
  }
}

//转换为Promise
export function toPromise(fn, config = {}) {
  return new Promise((success, fail) => fn({ success, fail, ...config }));
}

//打开模态框
export async function openModel(title, content, config = {}) {
  return toPromise(uni.showModal, { title, content, ...config }).then((res) => {
    if (res.cancel) return Promise.reject(res);
    else return Promise.resolve(res);
  });
}

/**
 * @description: 获取查询时的过滤条件
 * @param {object} data 查询条件({key:value})
 * @param {string} method 查询方法(eq,like,gt,lt,ge,le)
 * @param {string} type 查询类型(custom,or,and)
 * @return {array}
 */
export function getSearchFilter(
  data = {},
  { method = "eq", type = "custom" } = {}
) {
  const list = [];
  const get = (val) => (Array.isArray(val) ? val : [val]);
  for (const key in data) {
    const value = data[key];
    const params = { key, method, type, value };
    if (isObject(value)) Object.assign(params, value);
    params.value = get(params.value);
    list.push(params);
  }
  return list;
}

//dataInterface转getModelInfo的数据结构
export function interfaceToModel(result) {
  const { list = [], pagination = {} } = result.data[0] || {};
  return {
    code: result.code,
    msg: result.msg,
    data: {
      list,
      pagination: pagination[0] || {},
    },
  };
}

export function verifyLogin() {
  const token = storage.get("Token") || null;
  // 判断是否有路由权限
  if (token) return Promise.resolve();
  // if (token) return Promise.reject();
  else return verifyError("请登录后再操作!", "/subPages/user/login/login");
}

function verifyError(msg, url) {
  const duration = 650;
  message(msg, "none", { duration });
  return Promise.reject(function (fn) {
    setTimeout(() => {
      if (fn instanceof Function) fn();
      uni.navigateTo({ url });
    }, duration);
  });
}

const cacheState = new Map(); //缓存状态仓库
export function cacheRequestState(fn, key) {
  //缓存请求状态
  if (!key) key = fn.name || null; //如果没有key则使用函数名作为key
  if (key === null) throw new Error("cacheRequest: key is null");
  if (cacheState.has(key)) return cacheState.get(key);
  const result = new Promise((res, rej) => {
    let errIndex = 0;
    const reloadRequest = () => {
      fn()
        .then((o) => cacheState.delete(key, res(o))) //请求成功后缓存出库
        .catch((o) => (errIndex++ >= 3 ? rej(o) : reloadRequest())); //请求失败后重试3次
    };
    reloadRequest();
  });
  cacheState.set(key, result);
  return result;
}

/**
 * @description: 创建actions
 * @param {string} key state中的key
 * @param {function} fn 请求函数
 * @param {string} keName mutations中的key
 * @param {?boolean} vEmpty 是否严格校验空值(比如空数组,空对象)
 * @param {?function} filter 过滤函数
 * @return {object}
 */
const initKeys = []; //初始化过的key
export function createActions(key, [fn, keyName, vEmpty] = [], filter) {
  return async function (store, refresh) {
    const { state, commit } = store;
    let data = state[key],
      empty;
    if (vEmpty) {
      empty = verifyEmpty(data);
    } else {
      empty = !initKeys.includes(key);
      if (empty) initKeys.push(key);
    }
    if (refresh || empty) {
      //刷新或者空值时重新请求
      data = await cacheRequestState(fn.bind(this, store), keyName); //获取缓存请求状态
      if (isFunction(filter)) data = filter(data, store); //数据过滤
      commit(keyName, data); //提交数据
    }
    return data;
  };
}

async function fileApiFn(key, { url, filePath, _config = {} } = {}) {
  if (!uni.$u.test.url(url)) url = config.baseURL + url;
  return toPromise(uni[key], {
    url,
    filePath,
    name: "file",
    header: { Authorization: storage.get("Token") },
    ..._config,
  }).then((res) => Promise.resolve(res, filePath));
}

export const fileApi = {
  upload: (params) =>
    fileApiFn("uploadFile", params).then((o) => JSON.parse(o.data)),
  download: (params) =>
    fileApiFn("downloadFile", params).then((o, filePath) => {
      if (o.statusCode !== 200) throw { msg: "获取下载文件失败" };
      return o;
      return toPromise(uni.saveFile, {
        tempFilePath: o.tempFilePath,
        filePath: filePath,
      });
    }),
};

/**
 * @description: 缓存请求数据
 * @param {function} api 接口
 * @param {string} key 唯一标识符
 * @param {?time} [time=0] 缓存时间(为0缓存至版本更新)
 * @param {?updateClear} [updateClear=false] 版本更新时是否清除
 * @return {Promise}
 */
export async function cacheRequestData(api, key, params = {}) {
  const CacheKey = "Cache_" + key;
  const { time = 0, updateClear = false } = params;
  const { $cache_time, data: cacheData } = storage.get(CacheKey) || {}; //读取数据缓存
  const timestamp = Date.now();
  const expires = time && $cache_time && timestamp >= $cache_time + time; //是否过期
  const needUpdate = expires || verifyEmpty(cacheData); //是否更新
  if (!needUpdate) return cacheData;
  let data = api();
  if (data instanceof Promise) data = await data;
  if (data.code === 200) {
    //空数据缓存会影响页面,不缓存空数据
    if (updateClear || !$cache_time) {
      const clearList = storage.get("CacheRequestKeys") || [];
      if (!clearList.includes(key)) {
        clearList.push(key);
        storage.set("CacheRequestKeys", clearList);
      } //根据判断设置更新时清除请求缓存列表
    }
    storage.set(CacheKey, { data, $cache_time: timestamp }); //缓存数据
  }
  return data;
}
